package br.com.vfcfw.commons.util;

import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.naming.InitialContext;

import org.apache.log4j.Logger;
import org.jboss.weld.environment.se.StartMain;

/**
 * Classe utilitária para retornar a instâncias através do Weld.
 * 
 * @author Vitor Franco do Carmo (vitor.carmo@capgemini.com)
 * @since 27/05/2012
 */
public class WeldUtil {

    private static final Logger LOGGER = Logger.getLogger(WeldUtil.class);

    /**
     * Construtor.
     */
    private WeldUtil() {

        super();
    }

    /**
     * Retorna a instância do bean especificado pelo tipo.
     * 
     * @param classeBean Tipo do bean.
     * @return Instância do bean.
     */
    @SuppressWarnings("unchecked")
    public static <T> T obterBean(Class<T> classeBean) {

        T obj = null;

        LOGGER.debug(String.format("Obtendo bean da classe: %s.", classeBean.getSimpleName()));

        try {

            BeanManager beanManager = obterBeanManager();

            Bean<T> bean = (Bean<T>) beanManager.getBeans(classeBean).iterator().next();
            CreationalContext<T> context = beanManager.createCreationalContext(bean);
            obj = (T) beanManager.getReference(bean, classeBean, context);

        } catch (Exception e) {
            LOGGER.error(String.format("Erro ao obter bean da classe: %s.", classeBean.getSimpleName()), e);
        }

        return obj;
    }

    /**
     * Retorna a instância do bean especificado pelo nome.
     * 
     * @param nomeBean Nome do bean.
     * @return Instância do bean.
     */
    @SuppressWarnings("unchecked")
    public static <T> T obterBean(String nomeBean) {

        T obj = null;

        LOGGER.debug(String.format("Obtendo bean com o nome: %s.", nomeBean));

        try {

            BeanManager beanManager = obterBeanManager();

            Bean<T> bean = (Bean<T>) beanManager.getBeans(nomeBean).iterator().next();
            CreationalContext<T> context = beanManager.createCreationalContext(bean);
            obj = (T) beanManager.getReference(bean, bean.getClass(), context);

        } catch (Exception e) {
            LOGGER.error(String.format("Erro ao obter bean com nome: %s.", nomeBean), e);
        }

        return obj;
    }

    /**
     * Retorna a instância do bean especificado pelo tipo.
     * 
     * @param classeBean Tipo do bean.
     * @return Instância do bean.
     */
    @SuppressWarnings("unchecked")
    public static <T> T obterBeanParaTesteUnitario(Class<T> classeBean) {

        T obj = null;

        LOGGER.debug(String.format("Obtendo bean da classe: %s.", classeBean.getSimpleName()));

        try {

            BeanManager beanManager = obterBeanManagerParaTesteUnitario();

            Bean<T> bean = (Bean<T>) beanManager.getBeans(classeBean).iterator().next();
            CreationalContext<T> creationalContext = beanManager.createCreationalContext(bean);
            obj = (T) beanManager.getReference(bean, classeBean, creationalContext);

        } catch (Exception e) {
            LOGGER.error(String.format("Erro ao obter bean da classe: %s.", classeBean.getSimpleName()), e);
        }
        return obj;
    }

    /**
     * Retorna a instância do {@link BeanManager}.
     * 
     * @return {@link BeanManager}.
     */
    private static BeanManager obterBeanManager() {

        BeanManager beanManager = null;

        LOGGER.debug("Obtendo o BeanManager.");

        try {

            InitialContext initialContext = new InitialContext();
            beanManager = (BeanManager) initialContext.lookup("java:comp/BeanManager");

        } catch (Exception e) {
            LOGGER.error("Erro ao obter BeanManager.", e);
        }
        return beanManager;
    }

    /**
     * Retorna a instância do {@link BeanManager}.
     * 
     * @return {@link BeanManager}
     */
    private static BeanManager obterBeanManagerParaTesteUnitario() {

        BeanManager beanManager = null;

        LOGGER.debug("Obtendo o BeanManager.");

        try {

            StartMain startMain = new StartMain(new String[0]);
            beanManager = startMain.go().getBeanManager();

        } catch (Exception e) {
            LOGGER.error("Erro ao obter BeanManager.", e);
        }
        return beanManager;
    }
}